package agent;
/*_############################################################################
  _## 
  _##  SNMP4J-Agent 2 - Agente.java  
  _## 
  _##  Copyright (C) 2005-2012  Frank Fock (SNMP4J.org)
  _##  
  _##  Licensed under the Apache License, Version 2.0 (the "License");
  _##  you may not use this file except in compliance with the License.
  _##  You may obtain a copy of the License at
  _##  
  _##      http://www.apache.org/licenses/LICENSE-2.0
  _##  
  _##  Unless required by applicable law or agreed to in writing, software
  _##  distributed under the License is distributed on an "AS IS" BASIS,
  _##  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  _##  See the License for the specific language governing permissions and
  _##  limitations under the License.
  _##  
  _##########################################################################*/

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.snmp4j.TransportMapping;
import org.snmp4j.agent.BaseAgent;
import org.snmp4j.agent.CommandProcessor;
import org.snmp4j.agent.DuplicateRegistrationException;
import org.snmp4j.agent.io.ImportModes;
import org.snmp4j.agent.mo.DefaultMOFactory;
import org.snmp4j.agent.mo.MOTableRow;
import org.snmp4j.agent.mo.snmp.RowStatus;
import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB;
import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB;
import org.snmp4j.agent.mo.snmp.SnmpTargetMIB;
import org.snmp4j.agent.mo.snmp.StorageType;
import org.snmp4j.agent.mo.snmp.TransportDomains;
import org.snmp4j.agent.mo.snmp.VacmMIB;
import org.snmp4j.agent.security.MutableVACM;
import org.snmp4j.log.Log4jLogFactory;
import org.snmp4j.log.LogFactory;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.MessageProcessingModel;
import org.snmp4j.security.AuthSHA;
import org.snmp4j.security.PrivAES128;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModel;
import org.snmp4j.security.USM;
import org.snmp4j.security.UsmUser;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.smi.Variable;
import org.snmp4j.transport.TransportMappings;
import org.snmp4j.util.ThreadPool;

import agent.monitors.TorrentInterface;
import agent.monitors.TorrentMonitorImpl;

/**
 * The <code>Agente</code> is a sample SNMP agent implementation of all features (MIB implementations) provided by the SNMP4J-Agent framework. The <code>Agente</code> extends the <code>BaseAgent</code> which provides a framework for custom agent implementations through hook methods. Those abstract hook methods need to be implemented by extending the <code>BaseAgent</code>.
 * <p>
 * This IF-MIB implementation part of this test agent, is instrumentation as a simulation MIB. Thus, by changing the agentppSimMode (1.3.6.1.4.1.4976.2.1.1.0) from 'oper(1)' to 'config(2)' any object of the IF-MIB is writable and even creatable (columnar objects) via SNMP. Check it out!
 * 
 * @author Frank Fock
 * @version 1.0
 */
public class Agente extends BaseAgent {
	static {
		LogFactory.setLogFactory(new Log4jLogFactory());
	}
	
	protected String	address;
	private TorrentMIB	torrentMIB;
	
	/**
	 * Cria um agente com um arquivo para ler e armazenar o bootCounter e outro arquivo para ler e armazenar sua configuração.
	 * Também já cria uma pool de threads para gerenciar as solicitação do(s) gerente(s).
	 * 
	 * @param bootCounterFile
	 *            um arquivo contendo o bootCounter na forma serializada (como esperado pela classe BaseAgent).
	 * @param configFile
	 *            um arquivo de configuração com informação de gerencia serializada.
	 * @throws IOException
	 *             caso um dos dois arquivos não possam ser lidos da forma esperada.
	 */
	public Agente(File bootCounterFile, File configFile) throws IOException {
		super(bootCounterFile, configFile, new CommandProcessor(new OctetString(MPv3.createLocalEngineID())));
		this.agent.setWorkerPool(ThreadPool.create("RequestPool", 4));
	}
	
	/**
	 * Adiciona a MIB ao agente
	 */
	protected void registerSnmpMIBs() {
		this.torrentMIB = new TorrentMIB(DefaultMOFactory.getInstance());
		try {
			TorrentMonitorImpl.getInstance().startMonitor(new TorrentInterface("http://flmmartins.no-ip.org:8080"), this.torrentMIB);
		} catch (FileNotFoundException e) {
			System.err.println("Arquivo não encontrado.");
			System.exit(-1);
		}
		super.registerSnmpMIBs();
	}
	
	/**
	 * Adiciona a gerência de eventos à MIB
	 */
	protected void registerManagedObjects() {
		try {
			this.torrentMIB.registerMOs(server, null);
		} catch (DuplicateRegistrationException ex) {
			ex.printStackTrace();
		}
	}
	
	/**
	 * Remove a gerência de eventos à MIB
	 */
	protected void unregisterManagedObjects() {
		torrentMIB.unregisterMOs(server, null);
	}
	
	/**
	 * Configura serviço de notificações. Envia as TRAPS tanto usando protocolo SNMPv2c como SNMPv3.
	 */
	@Override
	protected void addNotificationTargets(SnmpTargetMIB targetMIB, SnmpNotificationMIB notificationMIB) {
		targetMIB.addDefaultTDomains();
		
		targetMIB.addTargetAddress(new OctetString("notificationV2c"), 	TransportDomains.transportDomainUdpIpv4, new OctetString(new UdpAddress("127.0.0.1/162").getValue()), 	200, 1, new OctetString("notify"), new OctetString("v2c"), 		StorageType.permanent);
		targetMIB.addTargetAddress(new OctetString("notificationV3"), 	TransportDomains.transportDomainUdpIpv4, new OctetString(new UdpAddress("127.0.0.1/1162").getValue()), 	200, 1, new OctetString("notify"), new OctetString("v3notify"),	StorageType.permanent);
		
		targetMIB.addTargetParams(new OctetString("v2c"), MessageProcessingModel.MPv2c, SecurityModel.SECURITY_MODEL_SNMPv2c, new OctetString("cpublicnotify"), SecurityLevel.AUTH_PRIV, StorageType.permanent);
		targetMIB.addTargetParams(new OctetString("v3notify"), MessageProcessingModel.MPv3, SecurityModel.SECURITY_MODEL_USM, new OctetString("v3notify"), SecurityLevel.AUTH_PRIV, StorageType.permanent);
		
		notificationMIB.addNotifyEntry(new OctetString("default"), new OctetString("notify"), SnmpNotificationMIB.SnmpNotifyTypeEnum.inform, StorageType.permanent);
	}
	
	/**
	 * Configura as visões de acesso. No caso, nos protocolos v1 e v2c 
	 */
	@Override
	protected void addViews(VacmMIB vacm) {
		//Grupos para fazer solicitações
		vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv1,  new OctetString("cpublic"), 		new OctetString("v1v2group"), 		StorageType.nonVolatile);
		vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c, new OctetString("cpublic"), 		new OctetString("v1v2group"), 		StorageType.nonVolatile);
		vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, 	new OctetString("SHAAES128"), 		new OctetString("v3group"), 		StorageType.nonVolatile);
		
		//Grupos usados para o serviço de TRAPS
		vacm.addGroup(SecurityModel.SECURITY_MODEL_SNMPv2c, new OctetString("cpublicnotify"), 	new OctetString("v1v2groupNotify"), StorageType.nonVolatile);
		vacm.addGroup(SecurityModel.SECURITY_MODEL_USM, 	new OctetString("v3notify"), 		new OctetString("v3group"), 		StorageType.nonVolatile);
		
		//Tipos de acesso
		vacm.addViewTreeFamily(new OctetString("fullReadView"), 	new OID("1.3"), new OctetString(), VacmMIB.vacmViewIncluded, StorageType.nonVolatile);
		vacm.addViewTreeFamily(new OctetString("fullWriteView"), 	new OID("1.3"), new OctetString(), VacmMIB.vacmViewIncluded, StorageType.nonVolatile);
		vacm.addViewTreeFamily(new OctetString("fullNotifyView"), 	new OID("1.3"), new OctetString(), VacmMIB.vacmViewIncluded, StorageType.nonVolatile);
		
		//Mapeamento das permições de acesso para cada subárvore com cada grupo 
		vacm.addAccess(new OctetString("v1v2group"), 		new OctetString("public"), 	SecurityModel.SECURITY_MODEL_ANY, SecurityLevel.NOAUTH_NOPRIV, 	MutableVACM.VACM_MATCH_EXACT, 	new OctetString("fullReadView"), 	new OctetString("fullWriteView"), 	new OctetString("fullNotifyView"), 	StorageType.nonVolatile);
		vacm.addAccess(new OctetString("v1v2groupNotify"), 	new OctetString(), 			SecurityModel.SECURITY_MODEL_ANY, SecurityLevel.NOAUTH_NOPRIV, 	MutableVACM.VACM_VIEW_INCLUDED, new OctetString("fullReadView"), 	new OctetString("fullWriteView"), 	new OctetString("fullNotifyView"), 	StorageType.nonVolatile);
		vacm.addAccess(new OctetString("v3group"), 			new OctetString(), 			SecurityModel.SECURITY_MODEL_USM, SecurityLevel.AUTH_PRIV, 		MutableVACM.VACM_MATCH_EXACT, 	new OctetString("fullReadView"), 	new OctetString("fullWriteView"), 	new OctetString("fullNotifyView"), 	StorageType.nonVolatile);
	}
	
	/**
	 * Configura o agente para aceitar solicitações SNMPv3 com os usuários "SHAAES128" e "v3notify". 
	 * Sendo que o usuário v3notify só é usado para TRAPS.
	 */
	protected void addUsmUser(USM usm) {
		UsmUser user;
		user = new UsmUser(new OctetString("SHAAES128"), AuthSHA.ID, new OctetString("SHAAES128AuthPassword"), PrivAES128.ID, new OctetString("SHAAES128PrivPassword"));
		usm.addUser(user.getSecurityName(), usm.getLocalEngineID(), user);

		user = new UsmUser(new OctetString("v3notify"), null, null, null, null);
		usm.addUser(user.getSecurityName(), null, user);
	}
	
	/**
	 * Configura o agente para aceitar solicitações SNMPv1 e SNMPv2c com a comunidade "public".
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	protected void addCommunities(SnmpCommunityMIB communityMIB) {
		Variable[] com2sec = new Variable[] { new OctetString("public"), 	// community name
				new OctetString("cpublic"), 								// security name
				getAgent().getContextEngineID(), 							// local engine ID
				new OctetString("public"), 									// default context name
				new OctetString(), 											// transport tag
				new Integer32(StorageType.nonVolatile), 					// storage type
				new Integer32(RowStatus.active) 							// row status
		};
		MOTableRow row = communityMIB.getSnmpCommunityEntry().createRow(new OctetString("public2public").toSubIndex(true), com2sec);
		communityMIB.getSnmpCommunityEntry().addRow(row);
	}
	
	/**
	 * Crias os sockets para poder se comunicar
	 */
	@SuppressWarnings("rawtypes")
	protected void initTransportMappings() throws IOException {
		transportMappings = new TransportMapping[1];
		Address addr = GenericAddress.parse(address);
		TransportMapping tm = TransportMappings.getInstance().createTransportMapping(addr);
		transportMappings[0] = tm;
	}
	
	/**
	 * Inicialização do agente
	 */
	public static void main(String[] args) {
		String address;
		if (args.length > 0) {
			address = args[0];
		} else {
			address = "0.0.0.0/161"; //Porta para escutar os requests
		}
		
		//Configurar o log do sistema
		BasicConfigurator.configure();
		Logger.getRootLogger().setLevel(Level.WARN); //Nível de log
		
		try {
			Agente agente = new Agente(new File("AgentBC.cfg"), new File("AgentConfig.cfg"));
			agente.address = address;
			agente.init();
			agente.loadConfig(ImportModes.REPLACE_CREATE);
			agente.addShutdownHook();
			
			//Permite acesso geral de leitura sem senha nem nada para as versões 1 e 2c do SNMP
			agente.getServer().addContext(new OctetString("public"));
			
			agente.finishInit();
			agente.run();
			agente.sendColdStartNotification();

			while (true) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException ex1) {
					break;
				}
			}
		} catch (IOException ex) {
			ex.printStackTrace();
		}
		
	}
}
